/*
 * Copyright 2013 Alan Burlison, alan@bleaklow.com.  All rights reserved.
 * Use is subject to license terms.  See LICENSE.txt for details.
 */

/*
 * Nordic nRF24L01+ radio driver.
 */

#ifndef nRF24L01plus_h
#define nRF24L01plus_h

#include "SPIMaster.h"

// Maximum size of a radio packet
#define RADIO_BUFSZ 32

// Commands.
#define R_REGISTER          0b00000000
#define W_REGISTER          0b00100000
#define R_RX_PAYLOAD        0b01100001
#define W_TX_PAYLOAD        0b10100000
#define FLUSH_TX            0b11100001
#define FLUSH_RX            0b11100010
#define REUSE_TX_PL         0b11100011
#define R_RX_PL_WID         0b01100000
#define W_ACK_PAYLOAD       0b10101000
#define W_TX_PAYLOAD_NO_ACK 0b10110000
#define NOP                 0b11111111

// Registers, register bits, register values, register masks.
#define REG_MASK       0x1F
#define ACK_PL_MASK    0x07

#define CONFIG         0x00
#define MASK_RX_DIR    6
#define MASK_TX_DS     5
#define MASK_MAX_RT    4
#define EN_CRC         3
#define CRCO           2
#define PWR_UP         1
#define PRIM_RX        0

#define EN_AA          0x01
#define ENAA_P5        5
#define ENAA_P4        4
#define ENAA_P3        3
#define ENAA_P2        2
#define ENAA_P1        1
#define ENAA_P0        0

#define EN_RXADDR      0x02
#define ERX_P5         5
#define ERX_P4         4
#define ERX_P3         3
#define ERX_P2         2
#define ERX_P1         1
#define ERX_P0         0

#define SETUP_AW       0x03
#define AW_3B          0b00000001
#define AW_4B          0b00000010
#define AW_5B          0b00000011

#define SETUP_RETR     0x04
#define ARD_GET(V)     (((uint8_t)(V) & 0xF0) >> 4)
#define ARD_SET(V)     (((uint8_t)(V) & 0x0F) << 4)
#define ARC_GET(V)     ((uint8_t)(V) & 0x0F)
#define ARC_SET(V)     ((uint8_t)(V) & 0x0F)

#define RF_CH          0x05
#define RF_CH_MASK     0b01111111

#define RF_SETUP       0x06
#define CONT_WAVE      7
#define RF_DR_LOW      5
#define PLL_LOCK       4
#define RF_DR_HIGH     3
#define RF_PWR_GET(V)  (((uint8_t)(V) & 0x14) >> 1)
#define RF_PWR_SET(V)  (((uint8_t)(V) & 0x03) << 1)

#define RADIO_STATUS   0x07
#define RX_DR          6
#define TX_DS          5
#define MAX_RT         4
#define RX_P_NO_GET(V) (((uint8_t)(V) & 0x14) >> 1)
#define TX_FULL        0

#define OBSERVE_TX     0x08
#define PLOS_CNT(R)    (((uint8_t)(R) & 0xF0) >> 4)
#define ARC_CNT(R)     ((uint8_t)(R) & 0x0F)

#define RPD            0x09

#define RX_ADDR_P0     0x0A

#define RX_ADDR_P1     0x0B

#define RX_ADDR_P2     0x0C

#define RX_ADDR_P3     0x0D

#define RX_ADDR_P4     0x0E

#define RX_ADDR_P5     0x0F

#define TX_ADDR        0x10

#define RX_PW_P0       0x11

#define RX_PW_P1       0x12

#define RX_PW_P2       0x13

#define RX_PW_P3       0x14

#define RX_PW_P4       0x15

#define RX_PW_P5       0x16

#define FIFO_STATUS    0x17
#define FIFO_TX_REUSE  6
#define FIFO_TX_FULL   5
#define FIFO_TX_EMPTY  4
#define FIFO_RX_FULL   1
#define FIFO_RX_EMPTY  0

#define DYNPD          0x1C
#define DPL_P5         5
#define DPL_P4         4
#define DPL_P3         3
#define DPL_P2         2
#define DPL_P1         1
#define DPL_P0         0

#define FEATURE        0x1D
#define EN_DPL         2
#define EN_AK_PAY      1
#define EN_DYN_ACK     0

class nRF24L01plus : public SPIMaster {
public:
    /*
     * Radio mode.
     */
    enum Mode { Disabled, Receive, Transmit };

    /*
     * Create a new radio driver.
     * _csn - Chip Select Not pin.
     * _ce - Chip Enable pin.
     */
    nRF24L01plus(const pin_t *const _csn, const pin_t *const _ce);

    /*
     * Create a new radio driver.
     * _csn - Chip Select Not pin.
     * _ce - Chip Enable pin.
     * _intr - interrups pin, NULL for none.
     */
    nRF24L01plus(const pin_t *const _csn, const pin_t *const _ce,
      const pin_t *const _intr);

    /*
     * Initialise the pins.
     * shared - initialise the shared SPI pins.
     */
    void initPins(bool shared);

    /*
     * Read a single-byte value from a register.
     * reg - the register to read.
     * return - the register value.
     */
    uint8_t r_register(uint8_t reg);

    /*
     * Read a multi-byte value from a register.
     * reg - the register to read.
     * value - the buffer to receive the value.
     * len - the length of the buffer.
     * return - the status byte.
     */
    uint8_t r_register(uint8_t reg, uint8_t *value, uint8_t len);

    /*
     * Write a single-byte value to a register.
     * reg - the register to write.
     * value - the value to write.
     * return - the status byte.
     */
    uint8_t w_register(uint8_t reg, uint8_t value);

    /*
     * Write a multi-byte value to a register.
     * reg - the register to write.
     * value - the buffer containing the value to write.
     * len - the length of the buffer.
     * return - the status byte.
     */
    uint8_t w_register(uint8_t reg, const uint8_t *value, uint8_t len);

    /*
     * Read from the receiver buffer.
     * value - the buffer to copy the data into.
     * len - the length of the buffer.
     * return - the status byte.
     */
    uint8_t r_rx_payload(uint8_t *value, uint8_t len);

    /*
     * Write to the transmit buffer.
     * value - the buffer containing the value to write.
     * len - the length of the buffer.
     * return - the status byte.
     */
    uint8_t w_tx_payload(const uint8_t *value, uint8_t len);

    /*
     * Flush the receive buffer.
     * return - the status byte.
     */
    uint8_t flush_tx();

    /*
     * Flush the transmit buffer.
     * return - the status byte.
     */
    uint8_t flush_rx();

    /*
     * Retransmit the current transmit buffer.
     * return - the status byte.
     */
    uint8_t reuse_tx_pl();

    /*
     * Read the received buffer size.
     * return - the received buffer size.
     */
    uint8_t r_rx_pl_wid();

    /*
     * Write an acknowledgement payload.
     * pipe - the pipe ID for the acknowledgement.
     * value - the buffer containing the value to write.
     * len - the length of the buffer.
     * return - the status byte.
     *
     */
    uint8_t w_ack_payload(uint8_t pipe, const uint8_t *value, uint8_t len);

    /*
     * Write to the transmit buffer without sending an acknowledgement.
     * value - the buffer containing the value to write.
     * len - the length of the buffer.
     * return - the status byte.
     */
    uint8_t w_tx_payload_no_ack(const uint8_t *value, uint8_t len);

    /*
     * Issue a NOP command - used to read the status byte.
     * return - the status byte.
     */
    uint8_t nop();

    /*
     * Set the radio mode.  Note we specifically disable all the advanced
     * features such as ShockBurst, auto-ack and retransmits, as currently the
     * only supported use of the radios is in 'broadcast' mode where a single
     * master transmits to multiple slaves - in this scenario we can't use
     * those features.
     */
    void setMode(Mode mode, uint8_t chan, const uint8_t *addr, uint8_t addrLen,
      uint8_t _msgLen);

    /*
     * Return true if the transmitter is ready.
     * return - true if the transmitter is ready.
     */
    bool txReady();

    /*
     * Transmit a message.
     * value - the buffer containing the value to write.
     * len - the length of the buffer.
     * return - the status byte.
     */
    uint8_t tx(const uint8_t *buf, uint8_t len);

    /*
     * Return true if the receiver is ready.
     * return - true if the receiver is ready.
     */
    bool rxReady();

    /*
     * Receive a message.
     * value - the buffer into which to copy the message.
     * len - the length of the buffer.
     * return - the status byte.
     */
    uint8_t rx(uint8_t *buf, uint8_t len);

private:
    const pin_t *const ce;    // The Chip Enable port.
    const pin_t *const intr;  // The Interrupt port.
    uint8_t msgLen;
};

#endif